package com.elasicsearch;

import com.elasicsearch.pojo.Item;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.StringTerms;
import org.elasticsearch.search.aggregations.metrics.avg.InternalAvg;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.query.FetchSourceFilter;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by
 *  增删索引  生成映射的demo
 * @author touni
 * @date 2019/4/24
 * @time 15:00
 */
@RunWith(SpringRunner.class)
@SpringBootTest(classes = ElasicsearchApp.class)
public class TestElasicsearch {

    @Autowired
    private ElasticsearchTemplate elasticsearchTemplate;



    @Autowired
    private ItemRepository itemRepository;

    /**
     * 创建索引   GET /item查看
     *
     */
    @Test
    public void testCreateIndex(){
        elasticsearchTemplate.createIndex(Item.class);
    }

    /**
     *  生成映射   GET /item查看
     */
    @Test
    public void testPutMapping(){
        elasticsearchTemplate.putMapping(Item.class);
    }

    /**
     * 删除索引  GET /item查看
     */
    @Test
    public void testDeleteIndex(){

        elasticsearchTemplate.deleteIndex("item");

    }

    /**
     * 向索引库添加数据     GET /item/_search查看
     */
    @Test
    public void testAddData(){
        Item item = new Item(1L, "小米手机7", " 手机",
                "小米", 3499.00,
                "http://image.leyou.com/13123.jpg");
        itemRepository.save(item);
    }

    /**
     * 批量添加
     */
    @Test
    public void addSomeData(){
        List<Item> list = new ArrayList<>();
        list.add(new Item(2L, "坚果手机R1", " 手机", "锤子", 3699.00,"http://image.leyou.com/123.jpg"));
        list.add(new Item(3L, "华为META10", " 手机", "华为", 4499.00, "http://image.leyou.com/3.jpg"));
        // 接收对象集合，实现批量新增
        itemRepository.saveAll(list);
    }


    /**
     * 查询索引库中的数据
     */
    @Test
    public void query(){
        // 查询全部，并按照价格降序排序
        Iterable<Item> items = this.itemRepository.findAll(Sort.by("price").descending());
        for (Item item : items) {
            System.out.println("item = " + item);
        }
    }


    /**
     * 自定义方法，通过自定义的接口中的方法来查询
     */
    @Test
    public void testDiyMethod(){
        List<Item> byPriceBetween = itemRepository.findByPriceBetween(3480, 3500);
        for (Item item : byPriceBetween){
            System.out.println(item);
        }
    }


    /**
     * - NativeSearchQueryBuilder：Spring提供的一个查询条件构建器，帮助构建json格式的请求体
     * - QueryBuilders.matchQuery("title", "小米手机")：利用QueryBuilders来生成一个查询。QueryBuilders提供了大量的静态方法，用于生成各种不同类型的查询
     *
     * - `Page<item>`：默认是分页查询，因此返回的是一个分页的结果对象，包含属性：
     *    - totalElements：总条数
     *    - totalPages：总页数
     *    - Iterator：迭代器，本身实现了Iterator接口，因此可直接迭代得到当前页的数据
     *
     */
    @Test
    public void search(){
        //构建查询条件
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
        //添加基本分词查询
        queryBuilder.withQuery(QueryBuilders.matchQuery("title","小米"));
        //搜索  获取搜索结果
        Page<Item> items = this.itemRepository.search(queryBuilder.build());
        //总条数
        long total = items.getTotalElements();
        System.out.println("总共的查询结果：" + total);
        for (Item item :items){ System.out.println(item); }
    }


    @Test
    public void testPagination(){
        //构建查询条件
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
        //条件基本的分词查询
        queryBuilder.withQuery(QueryBuilders.termQuery("category","手机"));
        //进行分页  Elasticsearch中的分页是从第0页开始
        int page = 0;
        int size = 2;
        queryBuilder.withPageable(PageRequest.of(page,size));
        //搜索获取结果
        Page<Item> search = this.itemRepository.search(queryBuilder.build());
        //总条数
        System.out.println("总条数："+search.getTotalElements());
        //总页数
        System.out.println("总页数："+search.getTotalPages());
        //当前页
        System.out.println("当前页："+search.getNumber());
        //每页大小
        System.out.println("每页大小是："+search.getSize());
        for (Item item:search){
            System.out.println(item);
        }
    }


    /**
     *
     * 对结果进行排序后输出
     */
    @Test
    public void searchAndSort(){
        // 构建查询条件
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
        // 添加基本分词查询
        queryBuilder.withQuery(QueryBuilders.termQuery("category", "手机"));

        // 排序
        queryBuilder.withSort(SortBuilders.fieldSort("price").order(SortOrder.ASC));

        // 搜索，获取结果
        Page<Item> items = this.itemRepository.search(queryBuilder.build());
        // 总条数
        long total = items.getTotalElements();
        System.out.println("总条数 = " + total);

        for (Item item : items) {
            System.out.println(item);
        }
    }


    /**
     * 桶就是分组，比如这里我们按照品牌brand进行分组
     * `AggregationBuilders`：聚合的构建工厂类。所有聚合都由这个类来构建
     * `AggregatedPage`：聚合查询的结果类。它是`Page<T>`的子接口
     *
     * `AggregatedPage`在`Page`功能的基础上，拓展了与聚合相关的功能，它其实就是对聚合结果的一种封装，大家可以参照聚合结果的JSON结构来看
     *
     */
    @Test
    public void testAgg(){
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
        // 不查询任何结果
        queryBuilder.withSourceFilter(new FetchSourceFilter(new String[]{""}, null));
        // 1、添加一个新的聚合，聚合类型为terms，聚合名称为brands，聚合字段为brand
        queryBuilder.addAggregation(
                AggregationBuilders.terms("brands").field("brand"));
        // 2、查询,需要把结果强转为AggregatedPage类型
        AggregatedPage<Item> aggPage = (AggregatedPage<Item>) this.itemRepository.search(queryBuilder.build());
        // 3、解析
        // 3.1、从结果中取出名为brands的那个聚合，
        // 因为是利用String类型字段来进行的term聚合，所以结果要强转为StringTerm类型
        StringTerms agg = (StringTerms) aggPage.getAggregation("brands");
        // 3.2、获取桶
        List<StringTerms.Bucket> buckets = agg.getBuckets();
        // 3.3、遍历
        for (StringTerms.Bucket bucket : buckets) {
            // 3.4、获取桶中的key，即品牌名称
            System.out.println(bucket.getKeyAsString());
            // 3.5、获取桶中的文档数量
            System.out.println(bucket.getDocCount());
        }
    }


    /**
     * 嵌套聚合，求平均值
     */
    @Test
    public void testSubAgg(){
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
        // 不查询任何结果
        queryBuilder.withSourceFilter(new FetchSourceFilter(new String[]{""}, null));
        // 1、添加一个新的聚合，聚合类型为terms，聚合名称为brands，聚合字段为brand
        queryBuilder.addAggregation(
                AggregationBuilders.terms("brands").field("brand")
                        .subAggregation(AggregationBuilders.avg("priceAvg").field("price")) // 在品牌聚合桶内进行嵌套聚合，求平均值
        );
        // 2、查询,需要把结果强转为AggregatedPage类型
        AggregatedPage<Item> aggPage = (AggregatedPage<Item>) this.itemRepository.search(queryBuilder.build());
        // 3、解析
        // 3.1、从结果中取出名为brands的那个聚合，
        // 因为是利用String类型字段来进行的term聚合，所以结果要强转为StringTerm类型
        StringTerms agg = (StringTerms) aggPage.getAggregation("brands");
        // 3.2、获取桶
        List<StringTerms.Bucket> buckets = agg.getBuckets();
        // 3.3、遍历
        for (StringTerms.Bucket bucket : buckets) {
            // 3.4、获取桶中的key，即品牌名称  3.5、获取桶中的文档数量
            System.out.println(bucket.getKeyAsString() + "，共" + bucket.getDocCount() + "台");

            // 3.6.获取子聚合结果：
            InternalAvg avg = (InternalAvg) bucket.getAggregations().asMap().get("priceAvg");
            System.out.println("平均售价：" + avg.getValue());
        }

    }














}
